package cn.jy.generate;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.jy.generate.enums.RelTypeEnum;
import cn.jy.generate.pojo.*;
import cn.jy.generate.pojo.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.antlr.v4.runtime.misc.NotNull;
import org.apache.commons.io.FileUtils;
import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.sql.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

import static cn.hutool.core.text.StrPool.COMMA;
import static cn.jy.generate.constant.DbConstant.getTableColumnSql;
import static cn.jy.generate.constant.DbConstant.getTableInfoSql;
import static cn.jy.generate.enums.MultiConditionQueryConditionEnum.getConditionByType;
import static cn.jy.generate.utils.ColumnUtil.changeIdToName;
import static cn.jy.generate.utils.ConvertUtil.*;
import static cn.jy.generate.utils.ConvertUtil.dbColumnName2JavaProperties;

/**
 * 生成代码
 * author: ljw
 */
@Log4j2
public class GenerateCodeApplication {

	/** 数据库配置 */
	protected static DbConfig dbConfig;
	/** 策略配置 */
	protected static StrategyConfig strategyConfig;

	// 配置哪个模板生成代码
	private static String CREATE_TEMPLATE_NAME = "jy_base/ms_base.json";
	// 默认需要生成的文件
	protected static List<String> DEFAULT_POINT_GENERAL_FILE = List.of(
		"client.flt", "clientFallback.flt", "clientImpl.flt", "controller.flt", "createQO.flt", "detail.vue", "dtlVO.flt", "editQO.flt",
			"entity.flt", "list.vue", "mapper_java.flt", "mapper_xml.flt", "pageQO.flt", "pageVO.flt", "PageVO.flt", "exportQO.flt", "QO.flt", "service.flt", "serviceImpl.flt",
			"VO.flt", "wrapper.flt", "Dto.flt", "personalController.flt", "personalService.flt", "personalServiceImpl.flt");

	// 删除数据库前缀
	private static List<String> REMOVE_PREFIX = List.of("foresee_", "esp_", "xh_", "base_", "mall_", "ms_");

	// 枚举-需要忽略的列
	private static String[] EXCLUDE_ENUM_COLUMNS = new String[] {
		"status", "is_deleted", "bill_status", "order_status", "audit_status", "source", "is_enabled", "create_source", "update_source", "is_default"
	};

	/**
	 * 需要生成多条件的主表
	 */
	static String[] INCLUDE_MULTI_QUERY_TABLE = new String[] {
//		"scrm_biz_archives"
	};

	@SneakyThrows
	public static void main(String[] args) {

		// 1.获取配置表父子关系
		Map<String, ConfTable> sonParentMap = new HashMap<>(); // 配置-子表名对应父表
		Map<String, List<ConfTable>> parentSonMap = new HashMap<>(); // 配置-父表名对应子表
		Map<String, ConfTable> selfMap = new HashMap<>(); // 配置-自包含
		getTableRelation(sonParentMap, parentSonMap, selfMap);


		// 2.数据库连接：获取表结构
		getDBStructure(selfMap);

		// 3.生成对应文件
		generateCode(sonParentMap, parentSonMap, selfMap);

		System.out.println("执行完成");

	}

	/**
	 * 生成多条件json.
	 */
	private static void generateMultiConditionQuery(Map<String, Map<String, Object>> tableCodeAndDataMap,
													Map<String, ConfTable> selfMap) throws IOException {
		// 生成
		String directory = "template/" + strategyConfig.getCreateTemplateName() + "/";
		ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader(directory);
		Configuration cfg = Configuration.defaultConfiguration();
		GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
		// 模板路径
		File tempPath = ResourceUtils.getFile("classpath:" + directory);


		String backend = strategyConfig.getGenerateBizPath() + strategyConfig.getBackEnd() + "/";

		// 获取模板下的所有flt文件
		List<File> templateFiles = getDirectoryFiles(tempPath, "multiCondition.flt");

		Map<String, Map<String, Object>> tableListMap = new HashMap<>();

		Arrays.stream(INCLUDE_MULTI_QUERY_TABLE).forEach(tableCode -> {
			// 当前主/子孙表数据
			Map<String, Object> shared = new HashMap<>();
			tableListMap.put(tableCode, shared);

			// 获取当前以及子表
			getSonTable(shared, tableCode, tableCodeAndDataMap, selfMap);
		});
		Arrays.stream(INCLUDE_MULTI_QUERY_TABLE).forEach(key -> {
			Map<String, Object> shared = tableCodeAndDataMap.get(key);

			for (File template : templateFiles) {
				// 生成文件
				String orgFileName = template.getName().replace(".flt", "");
				String fileName = removePrefixAndSuffix(orgFileName);
				String fileEnd = upperFirst(fileName);

				String module = (String) shared.get("module");

				String path = backend;
				if (StrUtil.isNotBlank(module)) {
					path += "/" + module + "/";
				}

				Template t = gt.getTemplate(template.getName());
				shared.forEach(t::binding);
				Map<String, Object> tableList = tableListMap.get(key);
				List<Object> tableListArray = new ArrayList<>(tableList.values());
				t.binding("tableList", tableListArray);

				String className = (String) shared.get("className");
				String createPath = path + "/bean/" + className + fileEnd + ".java";
				createFile(t, createPath);
			}
		});
	}

	/**
	 * 生成多条件json.
	 */
	private static void generateExportServiceImpl(Map<String, Map<String, Object>> tableCodeAndDataMap) throws IOException {
		// 生成
		String directory = "template/" + strategyConfig.getCreateTemplateName() + "/";
		ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader(directory);
		Configuration cfg = Configuration.defaultConfiguration();
		GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
		// 模板路径
		File tempPath = ResourceUtils.getFile("classpath:" + directory);
		// 获取模板下的所有flt文件
		List<File> templateFiles = getDirectoryFiles(tempPath, "exportServiceImpl.flt");


		String backend = strategyConfig.getGenerateDownloadPath();

		tableCodeAndDataMap.forEach((key, shared) -> {
			// 排除表
			List<String> excludeTableList = strategyConfig.getExcludeTable();
			if (excludeTableList == null || !excludeTableList.contains(key)) {
				//是否需要导出
				if (shared.containsKey("needExport")){
					String needExport = (String) shared.get("needExport");
					if (!"yes".equals(needExport)){
						return;
					}
				}else {
					return;
				}
				for (File template : templateFiles) {
					Template t = gt.getTemplate(template.getName());
					shared.forEach(t::binding);

					String className = (String) shared.get("className");

					// 生成文件
					String orgFileName = template.getName().replace(".flt", "");
					String fileName = removePrefixAndSuffix(orgFileName);
					String fileEnd = upperFirst(fileName);

					String createPath = backend + "/Export" + className + "ServiceImpl.java";

					createFile(t, createPath);

				}
			}
		});

	}


	private static void getSonTable(Map<String, Object> shared, String tableCode,
									Map<String, Map<String, Object>> tableCodeAndDataMap, Map<String, ConfTable> selfMap) {
		shared.put(tableCode, tableCodeAndDataMap.get(tableCode));

		// 子孙数据
		List<ConfTable> childrenList = selfMap.get(tableCode).getChildrenList();
		if (childrenList != null && !childrenList.isEmpty()) {
			for (ConfTable childTable : childrenList) {
				getSonTable(shared, childTable.getConfTableCode(), tableCodeAndDataMap, selfMap);
			}
		}
	}


	/**
	 * 生成代码
	 */
	private static void generateCode(Map<String, ConfTable> sonParentMap,
			 Map<String, List<ConfTable>> parentSonMap, Map<String, ConfTable> selfMap) throws IOException {

		Map<String, Map<String, Object>> tableCodeAndDataMap = new HashMap<>();
		Map<String, RenderingEnum> enumMap = new HashMap<>();
		Map<String, List<RenderingDict>> dictMap = new HashMap<>();

		// 获取所有共享参数
		generalData(sonParentMap, parentSonMap, selfMap, tableCodeAndDataMap, enumMap, dictMap);

		// 1.生成字典
		generalDict(dictMap);

		// 2.生成枚举
		generalEnum(enumMap);

		 //3.生成java等业务代码
		generalBusiness(tableCodeAndDataMap);

		// 4.生成json - 多条件查询
		generateMultiConditionQuery(tableCodeAndDataMap, selfMap);

		// 5.生成导出数据service
		generateExportServiceImpl(tableCodeAndDataMap);

	}

	/**
	 * 生成sql
	 *
	 * @param enumMap
	 * @throws FileNotFoundException
	 */
	private static void	generalDict(Map<String, List<RenderingDict>> enumMap) throws IOException {
		//if (strategyConfig.getExcludeTable() != null || strategyConfig.getPointGeneralFile() != null) {
		//	// 指定生成时不需要反复生成这些字典和枚举
		//	return;
		//}
		// 生成字典代码或者枚举sql
		String dictDirStr = "template/common/";
		ClasspathResourceLoader dictResourceLoader = new ClasspathResourceLoader(dictDirStr);
		Configuration cfg = Configuration.defaultConfiguration();
		// 路径
		File dictDir = ResourceUtils.getFile("classpath:" + dictDirStr);
		List<File> dictTempList = getDirectoryFiles(dictDir, "dict");
		GroupTemplate dictGt = new GroupTemplate(dictResourceLoader, cfg);

		enumMap.forEach((key, dictList) -> {
			for (File file : dictTempList) {
				Template dictTemplate = dictGt.getTemplate(file.getName());
				String orgFileName = file.getName().replace(".flt", "");
				String fileName = removePrefixAndSuffix(orgFileName);
				String fileEnd = upperFirst(fileName);
				String createPath = strategyConfig.getGenerateScriptPath() + "/sql/" + key + fileEnd + "Sql.sql";
				dictTemplate.binding("dictList", dictList);
				createFile(dictTemplate, createPath);
			}
		});
	}

	/**
	 * 生成枚举.
	 *
	 * @param enumMap
	 * @throws FileNotFoundException
	 */
	private static void	generalEnum(Map<String, RenderingEnum> enumMap) throws IOException {
		if (CollectionUtil.isNotEmpty(strategyConfig.getExcludeTable()) || CollectionUtil.isNotEmpty(strategyConfig.getPointGeneralFile())) {
			// 指定生成时不需要反复生成这些字典和枚举
			return;
		}
		// 生成字典代码或者枚举sql
		String enumDir = "template/common/";
		ClasspathResourceLoader enumResourceLoader = new ClasspathResourceLoader(enumDir);
		Configuration cfg = Configuration.defaultConfiguration();
		GroupTemplate enumGt = new GroupTemplate(enumResourceLoader, cfg);
		// 枚举路径
		File enumFile = ResourceUtils.getFile("classpath:" + enumDir);
		List<File> enumFiles = getDirectoryFiles(enumFile, "enum");
		enumMap.forEach((key, renderingEnum) -> {
			Template enumTemplate = enumGt.getTemplate(enumFiles.get(0).getName());
			String createPath = strategyConfig.getGenerateApiPath() + strategyConfig.getBackEnd() + "/" + renderingEnum.getModule() + "/enums/" + key + "Enum.java";
			enumTemplate.binding("enumClassName", key);
			enumTemplate.binding("columnType", renderingEnum.getColumnType());
			enumTemplate.binding("propertiesComment", renderingEnum.getPropertiesComment());
			enumTemplate.binding("enumPropertiesList", renderingEnum.getEnumPropertiesList());
			enumTemplate.binding("packagePath", strategyConfig.getPackagePath());
			enumTemplate.binding("module", renderingEnum.getModule());
			createFile(enumTemplate, createPath);
		});
	}

	/**
	 * 生成业务代码.
	 *
	 * @param tableCodeAndDataMap
	 * @throws FileNotFoundException
	 */
	protected static void generalBusiness(Map<String, Map<String, Object>> tableCodeAndDataMap) throws IOException {

		String directory = "template/" + strategyConfig.getCreateTemplateName() + "/";
		ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader(directory);
		Configuration cfg = Configuration.defaultConfiguration();
		GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
		// 模板路径
		File tempPath = ResourceUtils.getFile("classpath:" + directory);

		List<String> pointGeneralFileList =
			CollectionUtil.isEmpty(strategyConfig.getPointGeneralFile()) ? DEFAULT_POINT_GENERAL_FILE : strategyConfig.getPointGeneralFile();

		String backend = strategyConfig.getGenerateBizPath() + strategyConfig.getBackEnd() + "/";
		String apiBackend = strategyConfig.getGenerateApiPath() + strategyConfig.getBackEnd() + "/";

		// 获取模板下的所有flt文件
		List<File> templateFiles = getDirectoryFiles(tempPath, null);
		tableCodeAndDataMap.forEach((key, shared) -> {
			// 排除表
			List<String> excludeTableList = strategyConfig.getExcludeTable();
			if (excludeTableList == null || !excludeTableList.contains(key)) {
				for (File template : templateFiles) {
					Template t = gt.getTemplate(template.getName());
					shared.forEach(t::binding);

					String relType = (String) shared.get("relType");
					String className = (String) shared.get("className");
					String module = (String) shared.get("module");
					Boolean needPersonal = (Boolean) shared.get("needPersonal");

					// 生成文件
					String orgFileName = template.getName().replace(".flt", "");
					String fileName = removePrefixAndSuffix(orgFileName);
					String fileEnd = upperFirst(fileName);

					if (!pointGeneralFileList.contains(template.getName())) {
						// 只生成指定文件类型的文件
						continue;
					}

					// 子表且是business，则不生成
					if (orgFileName.contains("controller") && StrUtil.isNotBlank(relType)) {
						continue;
					} else if (orgFileName.contains("createQO") && StrUtil.isNotBlank(relType)) {
						continue;
					} else if (orgFileName.contains("createQO") && StrUtil.isNotBlank(relType)) {
						continue;
					} else if (orgFileName.contains("editQO") && StrUtil.isNotBlank(relType)) {
						continue;
					} else if ((orgFileName.contains("clientImpl") || orgFileName.contains("clientFallback") || orgFileName.contains("client")) && StrUtil.isNotBlank(relType)) {
						continue;
					} else if ((orgFileName.contains("exportServiceImpl"))) {
						continue;
					} else if (orgFileName.contains("personal") && !needPersonal) {
						// 个人的controller和service，必须要是个人的设置才生成
						continue;
					}

					String path = backend;
					String apiPath = apiBackend;
					if (StrUtil.isNotBlank(module)) {
						path += "/" + module + "/";
						apiPath += "/" + module + "/";
					}


					String createPath = null;
					if (orgFileName.contains("_xml")) {
						createPath = path + fileName.toLowerCase() + "/" + className + fileEnd + ".xml";
					} else if (orgFileName.contains("vue_")) {
						createPath = path + fileName.toLowerCase() + "/" + className + fileEnd + ".vue";
					} else if (orgFileName.contains("personalServiceImpl")) {
						createPath = path + "/service/impl/Personal" + className + "ServiceImpl.java";
					} else if (orgFileName.contains("serviceImpl")) {
						createPath = path + "/service/impl/" + className + fileEnd + ".java";
					} else if (orgFileName.contains("clientImpl")) {
						createPath = path + "/feign/" + className + "Client.java";
					} else if (orgFileName.contains("clientFallback")) {
						createPath = apiPath + "/feign/" + className + "ClientFallback.java";
					} else if (orgFileName.contains("client")) {
						createPath = apiPath + "/feign/I" + className + fileEnd + ".java";
					} else if (orgFileName.contains("personalService")) {
						createPath = path + "/service/IPersonal" + className + "Service.java";
					} else if (orgFileName.contains("service")) {
						createPath = path + "/service/I" + className + fileEnd + ".java";
					} else if (orgFileName.contains("exportQO.flt") || orgFileName.contains("pageQO") || orgFileName.contains("createQO")
							|| orgFileName.contains("editQO") || orgFileName.contains("QO")) {
						createPath = apiPath + "/qo/" + className + fileEnd + ".java";
					} else if (orgFileName.contains("dtlVO") || orgFileName.contains("VO")) {
						createPath = apiPath + "/vo/" + className + fileEnd + ".java";
					} else if (orgFileName.contains("dto")) {
						createPath = apiPath + "/dto/" + className + fileEnd + ".java";
					} else if (orgFileName.contains("personalController")) {
						// 是controller时，且是c端项目，则需要生成管理端和个人端的文件夹controller
						createPath = path + "/controller/personal/Personal" + className + "Controller.java";
					} else if (orgFileName.contains("controller")) {
						// 是controller时，且是c端项目，则需要生成管理端和个人端的文件夹controller
						createPath = path + "/controller/admin/" + className + "Controller.java";
					} else {
						if (orgFileName.contains("entity")) {
							fileEnd = "";
						}
						if (orgFileName.contains("entity") && "esp".equals(strategyConfig.getCreateTemplateName())) {
							// esp系统
							createPath = apiPath + "/" + fileName.toLowerCase() + "/" + className + fileEnd + ".java";
						} else {
							createPath = path + "/" + fileName.toLowerCase() + "/" + className + fileEnd + ".java";
						}
					}


					createFile(t, createPath);

				}
			}
		});
	}

	/**
	 * 生成数据
	 * @param sonParentMap
	 * @param parentSonMap
	 * @param selfMap
	 * @param tableCodeAndDataMap
	 * @param enumMap
	 */
	static void generalData(Map<String, ConfTable> sonParentMap,
                            Map<String, List<ConfTable>> parentSonMap, Map<String, ConfTable> selfMap,
                            Map<String, Map<String, Object>> tableCodeAndDataMap,
                            Map<String, RenderingEnum> enumMap, Map<String, List<RenderingDict>> dictMap) {
		selfMap.forEach((tableName, value) -> {
			Map<String, Object> shared = new HashMap<>();
			AtomicBoolean sonOnlyIntermediateBase = new AtomicBoolean(false);
			List<RenderingColumn> extraColumnList = new ArrayList<>();
			// 开放变量
			shared.put("ddlTableCode", tableName);
			shared.put("ddlTableAlias", generateTableCodeAlias(tableName));
			shared.put("ddlTableComment", value.getDdlTableComment());
			shared.put("ddlColumnList", value.getDdlColumnInfoList());
			shared.put("orginTable", value);
			// 策略
			shared.put("showTree", value.getShowTree());
			shared.put("needDisable", value.getNeedDisable());
			shared.put("needAudit", value.getNeedAudit());
			shared.put("needExport", value.getNeedExport());
			shared.put("needTenant", value.getNeedTenant());
			shared.put("needPersonal", value.getNeedPersonal());
			String uniquenessColumn = value.getUniquenessColumn();
			if (StrUtil.isNotBlank(uniquenessColumn)) {
				List<String> uniquenessColumnsDB = Arrays.asList(uniquenessColumn.split(COMMA));
				List<String> uniquenessColumnsJava = new ArrayList<>(uniquenessColumnsDB.size());
				for (int i = 0; i < uniquenessColumnsDB.size(); i++) {
					uniquenessColumnsJava.add(upperFirst(dbColumnName2JavaProperties(uniquenessColumnsDB.get(i))));
				}
				shared.put("uniquenessColumnDB", uniquenessColumnsDB);
				shared.put("upperUniquenessColumnJava", uniquenessColumnsJava);
			} else {
				shared.put("uniquenessColumnDB", null);
				shared.put("upperUniquenessColumnJava", null);
			}


			String className = dbTableName2ClassName(tableName, REMOVE_PREFIX);
			shared.put("packagePath", strategyConfig.getPackagePath());
			shared.put("className", className);
			shared.put("lowerFirstClassName", lowerFirst(className));
			shared.put("frontTableName", value.getFrontTableName());
			shared.put("relType", value.getRelType());
			shared.put("microServiceName", value.getMicroServiceName());
			String module = value.getModule();
			shared.put("module", module);
			shared.put("tableType", value.getTableType());
			shared.put("bizType", value.getBizType());
			shared.put("hasCodeColumn", value.getHasCodeColumn());
			shared.put("needRecycleBin", value.getNeedRecycleBin());
			shared.put("needDefault", value.getNeedDefault());

			List<String> listQueryColumnList = new ArrayList<>();
			if (StrUtil.isNotBlank(value.getListQueryColumn())) {
				listQueryColumnList.addAll(Arrays.asList(value.getListQueryColumn().split(COMMA)));
			}


			if (value.getChildrenList() == null || value.getChildrenList().isEmpty()) {
				shared.put("hasChildren",  0);
				shared.put("childrenList", null);
			} else {
				shared.put("hasChildren",  1);
				List<RenderingChildren> childrenList = new ArrayList<>();
				value.getChildrenList().forEach(children -> {
					RenderingChildren render = new RenderingChildren();
					render.setDdlTableCode(children.getConfTableCode());
					render.setDdlTableAlias(generateTableCodeAlias(children.getConfTableCode()));
					String cn = dbTableName2ClassName(children.getConfTableCode(), REMOVE_PREFIX);
					render.setClassName(cn);
					render.setPackagePath(strategyConfig.getPackagePath());
					render.setLowerFirstClassName(lowerFirst(cn));
					render.setDdlTableComment(children.getDdlTableComment());
					render.setHasChildren(children.getChildrenList() == null || children.getChildrenList().isEmpty() ? 0 : 1);
					render.setDdlMainTableId(children.getDdlMainTableId());
					render.setMainTableId(upperFirst(dbColumnName2JavaProperties(children.getDdlMainTableId())));

					render.setRelType(children.getRelType());
					if (!RelTypeEnum.INTERMEDIATE_BASE.getCode().equals(children.getRelType())) {
						// 非多对多 - 基础表
						sonOnlyIntermediateBase.set(true);
					} else {
						// 额外的列-多对多 - 基础表
						RenderingColumn extraColumn = new RenderingColumn();
						extraColumn.setOwnerTableAlias(generateTableCodeAlias(children.getConfTableCode()));
						extraColumn.setColumnName(dbColumnName2JavaProperties(children.getDdlRelMainTableId()));
						extraColumn.setDdlColumnName(children.getDdlRelMainTableId());
						extraColumn.setColumnType(StrUtil.isNotBlank(children.getRelMainTableIdType()) ? children.getRelMainTableIdType() : "String");
						extraColumn.setColumnComment(children.getConfTableComment());
						extraColumn.setIsExtraNameColumn(false);
						extraColumnList.add(extraColumn);
						// 额外的列-多对多 - 基础表-名称
						RenderingColumn extraColumnName = new RenderingColumn();
						extraColumnName.setOwnerTableAlias(generateTableCodeAlias(children.getConfTableCode()));
						extraColumnName.setColumnName(dbColumnName2JavaProperties(changeIdToName(children.getDdlRelMainTableId())));
						extraColumnName.setDdlColumnName(changeIdToName(children.getDdlRelMainTableId()));
						extraColumnName.setColumnType(StrUtil.isNotBlank(children.getRelMainTableIdType()) ? children.getRelMainTableIdType() : "String");
						extraColumnName.setColumnComment(children.getConfTableComment());
						extraColumnName.setIsExtraNameColumn(true);
						extraColumnList.add(extraColumnName);
					}

					// 中间表背后的关联表属性
					render.setRelTableCode(children.getRelTableCode());
					render.setRelTableAlias(generateTableCodeAlias(children.getRelTableCode()));
					render.setRelMainTableIdType(children.getRelMainTableIdType());
					render.setRelTableClassName(dbTableName2ClassName(children.getRelTableCode(), REMOVE_PREFIX));
					render.setRelTableLowerFirstClassName(lowerFirst(render.getRelTableClassName()));
					render.setRelTablePackagePath(children.getRelTablePackagePath());
					render.setDdlRelMainTableId(children.getDdlRelMainTableId());
					render.setRelMainTableId(upperFirst(dbColumnName2JavaProperties(children.getDdlRelMainTableId())));
					render.setIsMicroService(children.getIsMicroService());


					childrenList.add(render);
				});
				shared.put("childrenList", childrenList);
			}
			// 有子表且仅仅是基础表的情况
			shared.put("sonOnlyIntermediateBase", sonOnlyIntermediateBase.get());
			// 额外的列-一般关联中间表再关联主表使用
			shared.put("extraColumnList", extraColumnList);


			// 转化属性
			List<RenderingColumn> columnList = new ArrayList<>();
			value.getDdlColumnInfoList().forEach(ddlColumnInfo -> {
				RenderingColumn column = new RenderingColumn();
				column.setOwnerTableAlias(generateTableCodeAlias(tableName));
				column.setColumnName(dbColumnName2JavaProperties(ddlColumnInfo.getDdlColumnName()));
				column.setColumnComment(ddlColumnInfo.getDdlColumnComment());
				column.setColumnType(dbColumnType2JavaProperties(ddlColumnInfo.getDdlDataType()));

				column.setDdlColumnName(ddlColumnInfo.getDdlColumnName());
				column.setDdlDataType(ddlColumnInfo.getDdlDataType());
				column.setMultiList(getConditionByType(column.getColumnType()));
				column.setIsListQueryColumn(listQueryColumnList.contains(ddlColumnInfo.getDdlColumnName()) ? "yes" : "no");
				columnList.add(column);


				if (Arrays.stream(EXCLUDE_ENUM_COLUMNS).noneMatch(c -> c.equals(ddlColumnInfo.getDdlColumnName()))) {
					// 排除
					String ddlColumnComment = ddlColumnInfo.getDdlColumnComment();
					// 利用备注来截取后，生成字典代码或者枚举sql
					// 备注写法： 是否已删除：0-未删除-NOT_DELETED；1-已删除-IS_DELETED
					String[] split = ddlColumnComment.split("：");
					if (split.length > 1) {
						String cont = split[1];
						String[] contentArr = cont.split("；");
						if (contentArr.length > 1) {
							// 枚举
							String enumClassName = className + upperFirst(column.getColumnName());
							getRenderingEnumAndDict(ddlColumnComment, column, contentArr, enumClassName, enumMap, dictMap, module);

							// 此列是枚举
							column.setIsDict("yes");
							column.setDictCode(enumClassName);

						}
					}
				}
			});

			shared.put("columnList", columnList);

			shared.put("selfMap", selfMap);
			ConfTable parent = sonParentMap.get(tableName);
			if (parent != null) {
				String parentClassName = dbTableName2ClassName(parent.getConfTableCode(), REMOVE_PREFIX);
				shared.put("parent", parent);
				shared.put("parentClassName", parentClassName);
				shared.put("lowerFirstParentClassName", lowerFirst(parentClassName));
				// 子表对应的父子关系
				shared.put("parentSonRelType", value.getRelType());
				shared.put("mainTableId", upperFirst(dbColumnName2JavaProperties(value.getDdlMainTableId())));

				shared.put("relTableCode", value.getRelTableCode());
				shared.put("relTableAlias", generateTableCodeAlias(value.getRelTableCode()));
				shared.put("relTableClassName", dbTableName2ClassName(value.getRelTableCode(), REMOVE_PREFIX));
				shared.put("relTableLowerFirstClassName", lowerFirst(dbTableName2ClassName(value.getRelTableCode(), REMOVE_PREFIX)));
				// 中间表背后的关联表属性
				shared.put("relTablePackagePath", value.getRelTablePackagePath());
				shared.put("ddlRelMainTableId", value.getDdlRelMainTableId());
				shared.put("relMainTableId", upperFirst(dbColumnName2JavaProperties(value.getDdlRelMainTableId())));
				shared.put("relMainTableIdForName", upperFirst(dbColumnName2JavaProperties(changeIdToName(value.getDdlRelMainTableId()))));
				shared.put("isMicroService", value.getIsMicroService());
			} else {
				shared.put("parent", null);
				shared.put("parentClassName", null);
				shared.put("parentSonRelType", null);
				shared.put("lowerFirstParentClassName", null);
				shared.put("mainTableId", null);

				shared.put("relTableCode", null);
				shared.put("relTableAlias", null);
				shared.put("relTableClassName", null);
				shared.put("relTableLowerFirstClassName", null);

				shared.put("relTablePackagePath", null);
				shared.put("ddlRelMainTableId", null);
				shared.put("relMainTableIdForName", null);
				shared.put("relMainTableName", null);
				shared.put("isMicroService", null);
			}
			shared.put("parentSonMap", parentSonMap);

			tableCodeAndDataMap.put(tableName, shared);

		});
	}

	@NotNull
    static void getRenderingEnumAndDict(String ddlColumnComment,
                                        RenderingColumn column, String[] contentArr, String enumClassName,
                                        Map<String, RenderingEnum> enumMap, Map<String, List<RenderingDict>> dictMap,
                                        String module) {
		RenderingEnum renderingEnum = new RenderingEnum();
		renderingEnum.setPropertiesComment(ddlColumnComment);
		renderingEnum.setColumnType(column.getColumnType());
		renderingEnum.setModule(module);

		// 枚举属性
		List<RenderingEnum.EnumProperties> enumPropertiesList = new ArrayList<>();

		// 字典
		List<RenderingDict> dictList = new ArrayList<>();
		long parentId = IdUtil.getSnowflakeNextId();

		if (contentArr.length > 0) {
			RenderingDict dict = new RenderingDict();
			dict.setId(parentId);
			dict.setParentId(0L);
			dict.setCode(enumClassName);
			dict.setDictValue(column.getColumnComment());
			// TODO 应该去查询数据库，自动顺序
			dict.setSort(136);
			dict.setRemark(ddlColumnComment + column.getColumnComment());
			dictList.add(dict);
		}

		for (int i = 0; i < contentArr.length; i++) {
			String codeName = contentArr[i];
			String[] arr = codeName.split("-");
			if (arr.length <= 1)
				continue;

			// 枚举属性
			RenderingEnum.EnumProperties enumProperty = new RenderingEnum.EnumProperties();
			String code = arr[0];
			String name = arr[1];
			enumProperty.setCode(code);
			enumProperty.setName(name);
			String attribute = code.toUpperCase();
			if (arr.length > 2) {
				attribute = arr[2].toUpperCase();
			}
			enumProperty.setAttribute(attribute);

			// 字典
			RenderingDict dict = new RenderingDict();
			dict.setId(IdUtil.getSnowflakeNextId());
			dict.setParentId(parentId);
			dict.setCode(enumClassName);
			dict.setDictKey(code);
			dict.setDictValue(name);
			dict.setSort(i + 1);
			dict.setRemark(ddlColumnComment + column.getColumnComment());

			dictList.add(dict);


			enumPropertiesList.add(enumProperty);
		}

		renderingEnum.setEnumPropertiesList(enumPropertiesList);

		enumMap.put(enumClassName, renderingEnum);
		dictMap.put(enumClassName, dictList);
	}

	/**
	 * 生成文件.
	 *
	 * @param t 模板
	 * @param createPath 生成路径
	 */
	static void createFile(Template t, String createPath) {
		File file = new File(createPath);
		if (!file.exists()) {
			file.getParentFile().mkdirs();
		}
		try {
			FileWriter filterWriter = new FileWriter(file);
			t.renderTo(filterWriter);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 获取配置表父子关系.
	 *
	 * @param sonParentMap 子表和父亲表关系
	 * @param parentSonMap 父亲和子表关系
	 * @param selfMap 自包含
	 */
	static void getTableRelation(Map<String, ConfTable> sonParentMap,
                                 Map<String, List<ConfTable>> parentSonMap, Map<String, ConfTable> selfMap) {
		// 用ResourceUtils获取文件
		// 用commons.io获取文件json字符串
		// 用fastjson解析字符串
		try {
			// 获取finance_contract.json文件的json内容并解析出java类
			File file = ResourceUtils.getFile("classpath:definition/" + CREATE_TEMPLATE_NAME);
			String content = FileUtils.readFileToString(file, "UTF-8");
			String replace = content.replaceAll("\\s", "").replace("\\r\\n", "");
			JSONObject parse = JSON.parseObject(replace);

			List<ConfTable> confTableList = JSON.parseArray(parse.getString("tables"), ConfTable.class);
			dbConfig = JSON.parseObject(parse.getString("dbConfig"), DbConfig.class);
			strategyConfig = JSON.parseObject(parse.getString("strategyConfig"), StrategyConfig.class);
			for (ConfTable confTable : confTableList) {
				// 获取父子关系
				getChildren(confTable, sonParentMap, parentSonMap, selfMap);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}


	/**
	 * 获取父子关系.
	 *
	 * @param parentTable 父表
	 * @param sonParentMap 子表和父亲表关系
	 * @param parentSonMap 父亲和子表关系
	 * @param selfMap 自包含
	 */
	private static void getChildren(ConfTable parentTable, Map<String, ConfTable> sonParentMap,
			Map<String, List<ConfTable>> parentSonMap, Map<String, ConfTable> selfMap) {
		// 父表
		List<ConfTable> childrenList = parentTable.getChildrenList();
		selfMap.put(parentTable.getConfTableCode(), parentTable);

		// 子表
		if (childrenList != null && !childrenList.isEmpty()) {
			for (ConfTable child : childrenList) {
				// 子表对应父表
				sonParentMap.put(child.getConfTableCode(), parentTable);

				// 父表对应子表
				List<ConfTable> confTables = parentSonMap.get(parentTable.getConfTableCode());
				if (confTables == null) {
					confTables = new ArrayList<>();
				}
				ConfTable confTable = convert(child);
				confTable.setChildrenList(null);
				confTables.add(child);
				parentSonMap.put(parentTable.getConfTableCode(), confTables);

				// 递归获取子表
				getChildren(child, sonParentMap, parentSonMap, selfMap);
			}
		}
	}

	/**
	 * 转换.
	 *
	 * @param child 子表
	 * @return 新对象
	 */
	private static ConfTable convert(ConfTable child) {
		ConfTable newChild = new ConfTable();
		newChild.setConfTableCode(child.getConfTableCode());
		newChild.setConfTableComment(child.getConfTableComment());
		newChild.setFrontTableName(child.getFrontTableName());
		newChild.setMicroServiceName(child.getMicroServiceName());
		newChild.setModule(child.getModule());
		newChild.setTableType(child.getTableType());
		newChild.setBizType(child.getBizType());
		return newChild;
	}



	/**
	 * 获取数据库表真实结构.
	 */
	static void getDBStructure(Map<String, ConfTable> selfMap) throws SQLException, ClassNotFoundException {
		// 加载驱动
		Class.forName(dbConfig.getDbDriver());
		Connection connection = DriverManager.getConnection(dbConfig.getDbUrl(), dbConfig.getDbUserName(), dbConfig.getDbUserPassword());
		Statement statement = connection.createStatement();

		// 迭代表编码
		selfMap.forEach((confTableCode, table) -> {
            // 数据库连接
            ResultSet resultSet = null;
            try {
				// 表信息
                String tableExecuteSql = String.format(getTableInfoSql, confTableCode, dbConfig.getDbDatabase());
                resultSet = statement.executeQuery(tableExecuteSql);
                while (resultSet.next()) {
					table.setDdlTableSchema(resultSet.getString("TABLE_SCHEMA"));
					table.setDdlTableComment(resultSet.getString("TABLE_COMMENT"));
                }
				// 是否有code列
				String hasCodeColumn = "0";
				// 字段信息
				String columnExecuteSql = String.format(getTableColumnSql, confTableCode, dbConfig.getDbDatabase());
				resultSet = statement.executeQuery(columnExecuteSql);
				List<DDLColumnInfo> ddlColumnInfoList = new ArrayList<>();
				while (resultSet.next()) {
					DDLColumnInfo columnInfo = new DDLColumnInfo();
					columnInfo.setDdlColumnName(resultSet.getString("COLUMN_NAME"));
					columnInfo.setDdlOrdinalPosition(resultSet.getInt("ORDINAL_POSITION"));
					columnInfo.setDdlColumnDefault(resultSet.getString("COLUMN_DEFAULT"));
					columnInfo.setDdlIsNullable(resultSet.getString("IS_NULLABLE"));
					columnInfo.setDdlDataType(resultSet.getString("DATA_TYPE"));
					columnInfo.setDdlCharacterMaximumLength(resultSet.getLong("CHARACTER_MAXIMUM_LENGTH"));
					columnInfo.setDdlNumericPrecision(resultSet.getLong("NUMERIC_PRECISION"));
					columnInfo.setDdlNumericScale(resultSet.getLong("NUMERIC_SCALE"));
					columnInfo.setDdlColumnComment(resultSet.getString("COLUMN_COMMENT"));
					if ("code".equals(columnInfo.getDdlColumnName())) {
						hasCodeColumn = "1";
					}

					ddlColumnInfoList.add(columnInfo);
				}
				table.setDdlColumnInfoList(ddlColumnInfoList);
				// 是否有code列
				table.setHasCodeColumn(hasCodeColumn);

                resultSet.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
			}

        });

		statement.close();
		connection.close();
	}

	/**
	 * 获取目录下的所有文件.
	 *
	 * @param directory 目录
	 *
	 * @return 文件列表
	 */
	static List<File> getDirectoryFiles(File directory, String appoint) {
		List<File> fileList = new ArrayList<>();
		if (!directory.isDirectory()) {
			return fileList;
		}
		File[] files = directory.listFiles();
		if (files == null) {
			return fileList;
		}
		for (File file : files) {
			if (file.isDirectory()) {
				fileList.addAll(getDirectoryFiles(file, appoint));
			} else {
				String path = file.getPath();
				// 去除过滤结尾的值
				if (appoint != null && !path.contains(appoint)) {
					continue;
				}
				if (path.endsWith(".flt")) {
					fileList.add(file);
				}
			}
		}
		return fileList;
	}

}

